package com.youlin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.youlin.dto.*;
import com.youlin.dto.req.EditFHReqDto;
import com.youlin.entities.BygAppointment;
import com.youlin.entities.BygFarewellHall;
import com.youlin.entities.BygAppInfo;
import com.youlin.mapper.BygFarewellHallMapper;
import com.youlin.service.BygAppointmentService;
import com.youlin.service.BygFarewellHallService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlin.service.BygAppInfoService;
import com.youlin.untils.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
public class BygFarewellHallServiceImpl extends ServiceImpl<BygFarewellHallMapper, BygFarewellHall> implements BygFarewellHallService {

    @Autowired
    private BygAppInfoService bygUnitService;
    @Autowired
    private BygAppointmentService bygAppointmentService;
    @Autowired
    private BygAppInfoService bygAppInfoService;

    @Override
    public PageDataResDto<List<BygFarewellHall>> findPageFHs(Integer unitId, PageInfo pageInfo) {
        QueryWrapper<BygFarewellHall> queryWrapper = new QueryWrapper<BygFarewellHall>()
                .eq("unit_id", unitId).orderByDesc("add_time");
        IPage<BygFarewellHall> page = page(new Page<>(pageInfo.getPage(), pageInfo.getSize()), queryWrapper);
        List<BygFarewellHall> farewellHalls = page.getRecords();

        if (CollectionUtils.isNotEmpty(farewellHalls)) {
            List<Integer> fhIds = farewellHalls.parallelStream().map(BygFarewellHall::getId).collect(Collectors.toList());
            Map<Integer, List<BygAppointment>> amListMap = bygAppointmentService.list(
                    new QueryWrapper<BygAppointment>().select("fh_id").in("fh_id", fhIds))
                    .parallelStream().collect(Collectors.groupingBy(BygAppointment::getFhId));
            farewellHalls.parallelStream().forEach(fh -> {
                List<BygAppointment> appointments = amListMap.get(fh.getId());
                if (appointments != null) fh.setAmCount(appointments.size());
            });
        }

        pageInfo = new PageInfo(page.getCurrent(), page.getSize(), page.getTotal());
        return new PageDataResDto<List<BygFarewellHall>>(Status.SUCCESS).setData(farewellHalls).setPageInfo(pageInfo);
    }

    @Override
    @Transactional
    public BaseResDto<Status, String> editFH(EditFHReqDto editFHReqDto) {
        Integer unitId = editFHReqDto.getUnitId();
        QueryWrapper<BygFarewellHall> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("unit_id", unitId);
        if (editFHReqDto.getId() != null) queryWrapper.ne("id", editFHReqDto.getId());
        List<BygFarewellHall> fhs = list(queryWrapper);
        if (fhs.parallelStream().anyMatch(fh -> fh.getName().equals(editFHReqDto.getName())))
            return new BaseResDto<>(Status.FHNAMEDUPLICATE);
        BygFarewellHall farewellHall = BaseService.copyProperties(editFHReqDto, BygFarewellHall.class);

        if (farewellHall.getId() == null) { // 添加告别厅
            BygAppInfo unit = bygUnitService.getById(unitId);
            String code = String.valueOf(10000+unit.getAppId());
            for (int i = 1; i <= 20; i++) {
                int asInt = new Random().ints(100, 999).findAny().getAsInt();
                LambdaQueryWrapper<BygFarewellHall> wrapper = Wrappers.lambdaQuery(BygFarewellHall.class)
                        .eq(BygFarewellHall::getCode, Integer.valueOf(code+asInt));
                if (farewellHall.getId() == null) wrapper.ne(BygFarewellHall::getId, farewellHall.getId());
                if (count(wrapper) == 0) {
                    code += asInt;
                    break;
                }
            }
            farewellHall.setCode(Integer.valueOf(code));

            if (! save(farewellHall.setAddTime(LocalDateTime.now())))
                return new BaseResDto<>(Status.ERROR);

            CompletableFuture.runAsync(() -> {
                synchronized (this) {
                    BygAppInfo appInfo = bygAppInfoService.getById(unitId);
                    bygAppInfoService.updateById(appInfo.setFhCount(appInfo.getFhCount()+1));
                }
            });
        } else { // 修改告别厅
            if (! updateById(farewellHall.setUpdateTime(LocalDateTime.now())))
                return new BaseResDto<>(Status.ERROR);
        }
        return new BaseResDto<>(Status.SUCCESS);
    }

    @Override
    public BaseDataResDto<List<BygFarewellHall>> findAllFHs(Integer unitId) {
        LambdaQueryWrapper<BygFarewellHall> wrapper = Wrappers
                .lambdaQuery(BygFarewellHall.class).eq(BygFarewellHall::getUnitId, unitId);
        List<BygFarewellHall> farewellHalls = list(wrapper);
        return new BaseDataResDto<List<BygFarewellHall>>(Status.SUCCESS).setData(farewellHalls);
    }

    @Override
    @Transactional
    public BaseResDto<Status, String> delFHById(Integer fhId) {
        LambdaQueryWrapper<BygAppointment> lambdaQueryWrapper =
                Wrappers.lambdaQuery(BygAppointment.class).eq(BygAppointment::getFhId, fhId);
        if (bygAppointmentService.count(lambdaQueryWrapper) > 0)
            return new BaseResDto<>(Status.FH_AM_DEL_ERROR);
        BygFarewellHall farewellHall = getById(fhId);
        if (! removeById(fhId)) return new BaseResDto<>(Status.ERROR);

        CompletableFuture.runAsync(() -> {
            synchronized (this) {
                BygAppInfo appInfo = bygAppInfoService.getById(farewellHall.getUnitId());
                bygAppInfoService.updateById(appInfo.setFhCount(appInfo.getFhCount()-1));
            }
        });

        return new BaseResDto<>(Status.SUCCESS);
    }

}
